package mosdi.util;

import java.util.Arrays;

public class CodonAlphabet {

	private final static Alphabet dnaAlphabet = Alphabet.getDnaAlphabet();
	private final static Alphabet aaAlphabet = Alphabet.getAminoAcidAlphabet();
	private final static ProductEncoder encoder = new ProductEncoder(true,4,4,4);
	private final static int[] codonToAminoAcidTable = initTable();
	
	private static int[] initTable() {
		int[] result = new int[64];
		Arrays.fill(result, -1);
		result[getIndex(dnaAlphabet.buildIndexArray("ATG"))] = aaAlphabet.getIndex('M');
		result[getIndex(dnaAlphabet.buildIndexArray("GCT"))] = aaAlphabet.getIndex('A');
		result[getIndex(dnaAlphabet.buildIndexArray("GCC"))] = aaAlphabet.getIndex('A');
		result[getIndex(dnaAlphabet.buildIndexArray("GCA"))] = aaAlphabet.getIndex('A');
		result[getIndex(dnaAlphabet.buildIndexArray("GCG"))] = aaAlphabet.getIndex('A');
		result[getIndex(dnaAlphabet.buildIndexArray("TGT"))] = aaAlphabet.getIndex('C');
		result[getIndex(dnaAlphabet.buildIndexArray("TGC"))] = aaAlphabet.getIndex('C');
		result[getIndex(dnaAlphabet.buildIndexArray("GAA"))] = aaAlphabet.getIndex('E');
		result[getIndex(dnaAlphabet.buildIndexArray("GAG"))] = aaAlphabet.getIndex('E');
		result[getIndex(dnaAlphabet.buildIndexArray("GAT"))] = aaAlphabet.getIndex('D');
		result[getIndex(dnaAlphabet.buildIndexArray("GAC"))] = aaAlphabet.getIndex('D');
		result[getIndex(dnaAlphabet.buildIndexArray("GGT"))] = aaAlphabet.getIndex('G');
		result[getIndex(dnaAlphabet.buildIndexArray("GGC"))] = aaAlphabet.getIndex('G');
		result[getIndex(dnaAlphabet.buildIndexArray("GGA"))] = aaAlphabet.getIndex('G');
		result[getIndex(dnaAlphabet.buildIndexArray("GGG"))] = aaAlphabet.getIndex('G');
		result[getIndex(dnaAlphabet.buildIndexArray("TTT"))] = aaAlphabet.getIndex('F');
		result[getIndex(dnaAlphabet.buildIndexArray("TTC"))] = aaAlphabet.getIndex('F');
		result[getIndex(dnaAlphabet.buildIndexArray("ATT"))] = aaAlphabet.getIndex('I');
		result[getIndex(dnaAlphabet.buildIndexArray("ATC"))] = aaAlphabet.getIndex('I');
		result[getIndex(dnaAlphabet.buildIndexArray("ATA"))] = aaAlphabet.getIndex('I');
		result[getIndex(dnaAlphabet.buildIndexArray("CAT"))] = aaAlphabet.getIndex('H');
		result[getIndex(dnaAlphabet.buildIndexArray("CAC"))] = aaAlphabet.getIndex('H');
		result[getIndex(dnaAlphabet.buildIndexArray("AAA"))] = aaAlphabet.getIndex('K');
		result[getIndex(dnaAlphabet.buildIndexArray("AAG"))] = aaAlphabet.getIndex('K');
		result[getIndex(dnaAlphabet.buildIndexArray("ATG"))] = aaAlphabet.getIndex('M');
		result[getIndex(dnaAlphabet.buildIndexArray("TTA"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("TTG"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("CTT"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("CTC"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("CTA"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("CTG"))] = aaAlphabet.getIndex('L');
		result[getIndex(dnaAlphabet.buildIndexArray("AAT"))] = aaAlphabet.getIndex('N');
		result[getIndex(dnaAlphabet.buildIndexArray("AAC"))] = aaAlphabet.getIndex('N');
		result[getIndex(dnaAlphabet.buildIndexArray("CAA"))] = aaAlphabet.getIndex('Q');
		result[getIndex(dnaAlphabet.buildIndexArray("CAG"))] = aaAlphabet.getIndex('Q');
		result[getIndex(dnaAlphabet.buildIndexArray("CCT"))] = aaAlphabet.getIndex('P');
		result[getIndex(dnaAlphabet.buildIndexArray("CCC"))] = aaAlphabet.getIndex('P');
		result[getIndex(dnaAlphabet.buildIndexArray("CCA"))] = aaAlphabet.getIndex('P');
		result[getIndex(dnaAlphabet.buildIndexArray("CCG"))] = aaAlphabet.getIndex('P');
		result[getIndex(dnaAlphabet.buildIndexArray("TCT"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("TCC"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("TCA"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("TCG"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("AGT"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("AGC"))] = aaAlphabet.getIndex('S');
		result[getIndex(dnaAlphabet.buildIndexArray("CGT"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("CGC"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("CGA"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("CGG"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("AGA"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("AGG"))] = aaAlphabet.getIndex('R');
		result[getIndex(dnaAlphabet.buildIndexArray("ACT"))] = aaAlphabet.getIndex('T');
		result[getIndex(dnaAlphabet.buildIndexArray("ACC"))] = aaAlphabet.getIndex('T');
		result[getIndex(dnaAlphabet.buildIndexArray("ACA"))] = aaAlphabet.getIndex('T');
		result[getIndex(dnaAlphabet.buildIndexArray("ACG"))] = aaAlphabet.getIndex('T');
		result[getIndex(dnaAlphabet.buildIndexArray("TGG"))] = aaAlphabet.getIndex('W');
		result[getIndex(dnaAlphabet.buildIndexArray("GTT"))] = aaAlphabet.getIndex('V');
		result[getIndex(dnaAlphabet.buildIndexArray("GTC"))] = aaAlphabet.getIndex('V');
		result[getIndex(dnaAlphabet.buildIndexArray("GTA"))] = aaAlphabet.getIndex('V');
		result[getIndex(dnaAlphabet.buildIndexArray("GTG"))] = aaAlphabet.getIndex('V');
		result[getIndex(dnaAlphabet.buildIndexArray("TAT"))] = aaAlphabet.getIndex('Y');
		result[getIndex(dnaAlphabet.buildIndexArray("TAC"))] = aaAlphabet.getIndex('Y');
		return result;
	}
	
	public static int size() {
		return 64;
	}
	
	/** Returns the codon corresponding to the given index encoded by Alphabet.getDnaAlphabet. */
	public static int[] get(int index) {
		return encoder.decode(index);
	}

	/** Returns the index of a given codon, i.e. a number between 0 and 63. */
	public static int getIndex(String codon) {
		return getIndex(dnaAlphabet.buildIndexArray(codon));
	}
	
	/** Expects a codon encoded by Alphabet.getDnaAlphabet and returns the codon index, 
	 *  i.e. a number between 0 and 63. Returns -1 if given codon contains a -1.*/
	public static int getIndex(int[] codon) {
		if (codon.length!=3) throw new IllegalArgumentException();
		if ((codon[0]==-1) || (codon[1]==-1) || (codon[2]==-1)) return -1;
		return encoder.encode(codon);
	}

	/** Returns amino acid (as encoded by Alphabet.getAminoAcidAlphabet) for a given 
	 *  codon index (as encoded by getIndex). Returns -1 for a stop codon. */
	public static int getAminoAcid(int codonIndex) {
		return codonToAminoAcidTable[codonIndex];
	}
	
	public static int[] dnaToCodonText(int[] dnaText) {
		if (dnaText.length%3 != 0) throw new IllegalArgumentException("Length is not divisable by three.");
		int[] result = new int[dnaText.length/3];
		int n = 0;
		for (int i=0; i<result.length; ++i) {
			result[i] = encoder.encode(dnaText[n], dnaText[n+1], dnaText[n+2]); 
			n += 3;
		}
		return result;
	}

	public static int[] codonToDnaText(int[] codonText) {
		int[] result = new int[codonText.length*3];
		for (int i=0; i<codonText.length; ++i) {
			System.arraycopy(encoder.decode(codonText[i]), 0, result, i*3, 3);
		}
		return result;
	}

}
